﻿using KYSharp.SM;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.IO;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Ocsp;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using up7.db.model;

namespace up7.db.utils
{
    public class CryptoTool
    {
        //默认与up7.js.security.key解密后保持一致
        string key = "2C4ED1CC9BAA42A9";
        string iv  = "2C4ED1CC9BAA42A9";

        /// <summary>
        /// aes-cbc-解密
        /// </summary>
        /// <param name="txt"></param>
        /// <param name="key"></param>
        /// <param name="iv"></param>
        /// <returns></returns>
        public string decode(string txt)
        {
            byte[] buf = Convert.FromBase64String(txt);

            int lenAlign = 0;//对齐长度
            //16字节对齐
            if ((buf.Length % 16) > 0) lenAlign = 16 - buf.Length % 16;
            MemoryStream ms = new MemoryStream();
            //字节对齐
            ms.SetLength(buf.Length + lenAlign);
            ms.Write(buf, 0, buf.Length);

            byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
            byte[] ivArray = UTF8Encoding.UTF8.GetBytes(iv);

            RijndaelManaged rDel = new RijndaelManaged();
            rDel.Key = keyArray;
            rDel.IV = ivArray;
            rDel.Mode = CipherMode.CBC;
            rDel.Padding = PaddingMode.Zeros;

            ICryptoTransform cTransform = rDel.CreateDecryptor();
            byte[] ori = cTransform.TransformFinalBlock(ms.ToArray(), 0, (int)ms.Length);

            return UTF8Encoding.UTF8.GetString(ori, 0, buf.Length).Trim();
        }

        public MemoryStream decode(string aligo, Stream s)
        {
            if(aligo.Equals("sm4")) return this.sm4_decode(s);
            else if (aligo.Equals("aes/ecb")) return this.aes_ecb_decode(s);
            return this.aes_decode(s);
        }

        /// <summary>
        /// aes/cbc/pkcs
        /// </summary>
        /// <param name="stm">加密时此大小是16字节对齐</param>
        /// <param name="lenOri">原始块长度</param>
        /// <returns></returns>
        private MemoryStream aes_decode(Stream s)
        {
            var keys = UTF8Encoding.UTF8.GetBytes(this.key);
            var key = ParameterUtilities.CreateKeyParameter("AES", keys);
            var cipher = CipherUtilities.GetCipher("AES/CBC/PKCS5Padding");
            cipher.Init(false, new ParametersWithIV(key, keys));
            var outs = cipher.DoFinal(UtilsTool.toBytes(s));

            var ms = new System.IO.MemoryStream(outs);
            return ms;
        }

        private MemoryStream aes_ecb_decode(Stream s)
        {
            var keys = UTF8Encoding.UTF8.GetBytes(this.key);
            var key = ParameterUtilities.CreateKeyParameter("AES", keys);
            var cipher = CipherUtilities.GetCipher("AES/ECB/PKCS5Padding");
            cipher.Init(false, new ParametersWithIV(key, keys));
            var outs = cipher.DoFinal(UtilsTool.toBytes(s));

            var ms = new System.IO.MemoryStream(outs);
            return ms;
        }

        private MemoryStream sm4_decode(Stream s)
        {
            var keys = UTF8Encoding.UTF8.GetBytes(this.key);
            var key = ParameterUtilities.CreateKeyParameter("SM4", keys);
            var cipher = CipherUtilities.GetCipher("SM4/CBC/PKCS5Padding");
            cipher.Init(false, new ParametersWithIV(key,keys));
            var outs = cipher.DoFinal(UtilsTool.toBytes(s));

            var ms = new System.IO.MemoryStream(outs);
            return ms;
        }

        /// <summary>
        /// SM4/CBC/PKCS5Padding/base64
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public string sm4_decode(string s)
        {
            var keys = UTF8Encoding.UTF8.GetBytes(this.key);
            var key = ParameterUtilities.CreateKeyParameter("SM4", keys);
            var cipher = CipherUtilities.GetCipher("SM4/CBC/PKCS5Padding");
            cipher.Init(false, new ParametersWithIV(key, keys));

            var bts = Convert.FromBase64String(s);
            var outs = cipher.DoFinal(bts);

            return UTF8Encoding.UTF8.GetString(outs);
        }

        /// <summary>
        /// SM4/CBC/PKCS5Padding/base64
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public string sm4_encode(string s)
        {
            var keys = UTF8Encoding.UTF8.GetBytes(this.key);
            var key = ParameterUtilities.CreateKeyParameter("SM4", keys);
            var cipher = CipherUtilities.GetCipher("SM4/CBC/PKCS5Padding");
            cipher.Init(true, new ParametersWithIV(key, keys));

            var bts = UTF8Encoding.UTF8.GetBytes(s);
            var outs = cipher.DoFinal(bts);

            return Convert.ToBase64String(outs);
        }

        /// <summary>
        /// 完善逻辑，补齐文本长度，必须是16的倍数
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        public string encode(string v)
        {
            byte[] src = UTF8Encoding.UTF8.GetBytes(v);
            int lenAlign = 0;//对齐长度
            //16字节对齐
            if ((src.Length % 16) > 0) lenAlign = 16 - src.Length % 16;
            MemoryStream ms = new MemoryStream();
            ms.SetLength(src.Length + lenAlign);
            ms.Write(src, 0, src.Length);

            byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
            byte[] ivArray = UTF8Encoding.UTF8.GetBytes(iv);

            RijndaelManaged rDel = new RijndaelManaged();
            rDel.Key = keyArray;
            rDel.IV = ivArray;
            rDel.Mode = CipherMode.CBC;
            rDel.Padding = PaddingMode.Zeros;

            ICryptoTransform tf = rDel.CreateEncryptor();
            byte[] resultArray = tf.TransformFinalBlock(ms.ToArray(), 0, (int)ms.Length);

            return Convert.ToBase64String(resultArray, Base64FormattingOptions.None);
        }

        /// <summary>
        /// 计算token，进行授权检查
        /// token = encode( md5(id+nameLoc))
        /// </summary>
        /// <param name="f"></param>
        /// <param name="action">动作：init,block,cmp</param>
        /// <returns></returns>
        public string token(FileInf f, string action = "init")
        {
            string str = f.id + f.nameLoc + action;
            if (action == "block") str = f.id + f.pathSvr + action;
            str = this.md5(str);
            str = this.encode(str);
            return str;
        }

        public string md5(string s)
        {
            byte[] sor = Encoding.UTF8.GetBytes(s);
            MD5 md5 = MD5.Create();
            byte[] result = md5.ComputeHash(sor);
            StringBuilder strbul = new StringBuilder(40);
            for (int i = 0; i < result.Length; i++)
            {
                strbul.Append(result[i].ToString("x2"));//加密结果"x2"结果为32位,"x3"结果为48位,"x4"结果为64位

            }
            return strbul.ToString();
        }
    }
}